from __future__ import absolute_import
from . import message
from scopt.mpu.business.db import transaction
from scopt.util import message as msg_util
from scopt.util.message_types import INTEGER, STRING, BOOLEAN
from scopt.util.sqlalchemy_util import none, not_none


def _business_partner_maping():
    return {'id': 'm01_bp.id',
            'name': 'm01_bp.desc',
            'is_customer': 'm01_cust.bp_id',
            'is_supplier': 'm01_s.bp_id'}


def _media_type_model_mapping():
    return {'business_partner_id': 'm01_bp_med_con.bp_id',
            'media_type_id': 'm01_bp_med_con.med_type_id',
            'media_contact_id': 'm01_bp_med_con.med_con_id',
            'note': 'm01_bp_med_con.note'}


def _request_model_mapping():
    return {'business_partner_id': 'm01_bp_add.bp_id',
            'address_id': 'm01_bp_add.add_id',
            'address_type_id': 'm01_bp_add.add_type_id',
            'note': 'm01_bp_add.note'}


@message(type='settings.business_partner_create',
         version='0.1',
         reqd={'name': STRING(required=True),
               'is_customer': BOOLEAN(required=True),
               'is_supplier': BOOLEAN(required=True)},
         resd={'id': INTEGER(required=True)})
@transaction(['lds/BDS'])
def create(data_packet):
    db = msg_util.get_db(data_packet)
    reqd = msg_util.get_request(data_packet)['reqd']
    result = db.insert('m01_bp',
                       values=reqd,
                       mapping=_business_partner_maping())
    data_packet = msg_util.insert_response(data_packet,
                                           result,
                                           create,
                                           {'id': result})
    if msg_util.result_is_error(result):
        return data_packet
    # Since generated id is unique in parent table, unique key violation
    # is not expected here. No need to check, just go ahead.
    if reqd.get('is_customer') is not None and reqd.get('is_customer'):
        db.insert('m01_cust',
                  values={'bp_id': result})
    if reqd.get('is_supplier') is not None and reqd.get('is_supplier'):
        db.insert('m01_s',
                  values={'bp_id': result})
    return data_packet


@message(type='settings.business_partner_select',
         version='0.1',
         reqd={'id': INTEGER(required=False),
               'name': STRING(required=False),
               'is_customer': BOOLEAN(required=False),
               'is_supplier': BOOLEAN(required=False)},
         resd=[{'id':INTEGER(required=True),
                'name':STRING(required=True),
                'is_customer':BOOLEAN(required=True),
                'is_supplier':BOOLEAN(required=True)}])
@transaction(['lds/BDS'])
def select(data_packet):
    db = msg_util.get_db(data_packet)
    request = msg_util.get_request(data_packet)
    request = _map_customer_supplier(request)
    resd = db.query(['m01_bp', 'm01_cust', 'm01_s'],
                    where=db.and_(request['reqd'], 'id', 'name',
                                  'is_customer', 'is_supplier'),
                    mapping=_business_partner_maping(),
                    result_mapper=_map_result)
    return msg_util.ok_response(data_packet, select, resd)


def _map_customer_supplier(request):
    reqd = request['reqd']
    if reqd.get('is_customer') is not None:
        if reqd.get('is_customer'):
            reqd['is_customer'] = not_none()
        else:
            reqd['is_customer'] = none()

    if reqd.get('is_supplier') is not None:
        if reqd.get('is_supplier'):
            reqd['is_supplier'] = not_none()
        else:
            reqd['is_supplier'] = none()
    return request


def _map_result(result):
    return [{'id': row['m01_bp_id'],
             'name': row['m01_bp_desc'],
             'is_customer': row['m01_cust_bp_id'] is not None,
             'is_supplier': row['m01_s_bp_id'] is not None}
            for row in result]


@message(type='settings.business_partner_update',
         version='0.1',
         reqd={'id': INTEGER(required=True),
               'name': STRING(required=False),
               'is_customer': BOOLEAN(required=False),
               'is_supplier': BOOLEAN(required=False)},
         resd={})
@transaction(['lds/BDS'])
def update(data_packet):
    db = msg_util.get_db(data_packet)
    reqd = msg_util.get_request(data_packet)['reqd']
    partner_id = reqd.get('id')
    result = None
    if reqd.get('name') is not None:
        result = db.update_by_pk('m01_bp',
                                 partner_id,
                                 {'desc': reqd['name']})
        data_packet = msg_util.update_response(data_packet, result, update)
        if msg_util.result_is_error(result):
            return data_packet
    if reqd.get('is_customer') is not None:
        if reqd['is_customer']:
            # We can safely ignore IntegrityError
            db.insert('m01_cust',
                      values={'bp_id': reqd['id']})
        else:
            db.delete('m01_cust',
                      where=[['m01_cust.bp_id', partner_id]])
    if reqd.get('is_supplier') is not None:
        if reqd['is_supplier']:
            # We can safely ignore IntegrityError
            db.insert('m01_s',
                      values={'bp_id': reqd['id']})
        else:
            db.delete('m01_s',
                      where=[['m01_s.bp_id', partner_id]])
    return msg_util.update_response(data_packet, result, update)


##### BUSINESS PARTNER ADDRESS ################################################
@message(type='settings.business_partner_add_address',
         version='0.1',
         reqd={'business_partner_id': INTEGER(required=True),
               'address_id': INTEGER(required=True),
               'address_type_id': INTEGER(required=True),
               'note': STRING(required=False)},
         resd={'address_id': INTEGER(required=True),
               'business_partner_id': INTEGER(required=True)})
@transaction(['lds/BDS'])
def add_address(data_packet):
    db = msg_util.get_db(data_packet)
    reqd = msg_util.get_request(data_packet)['reqd']
    result = db.insert('m01_bp_add',
                       values=reqd,
                       mapping=_request_model_mapping())
    return msg_util.insert_response(data_packet,
                                    result,
                                    add_address,
                                    {'address_id':
                                        reqd['address_id'],
                                     'business_partner_id':
                                        reqd['business_partner_id']})


@message(type='settings.business_partner_address_list',
         version='0.1',
         reqd={'business_partner_id': INTEGER(required=True)},
         resd=[{'business_partner_id': INTEGER(required=True),
                'address': {'id': INTEGER(),
                            'address_line_1': STRING(),
                            'address_line_2': STRING(),
                            'address_line_3': STRING(),
                            'city': STRING(),
                            'state': STRING(),
                            'country': STRING(),
                            'zip': STRING()},
                'address_type': {'id': INTEGER(),
                                 'name': STRING(required=True)},
                'note': STRING()}])
@transaction(['lds/BDS'])
def address_list(data_packet):
    db = msg_util.get_db(data_packet)
    reqd = msg_util.get_request(data_packet)['reqd']
    resd = db.query(['m01_bp_add', 'm01_add', 'm01_add_type'],
                    where=db.and_(reqd, 'business_partner_id'),
                    mapping=_request_model_mapping(),
                    result_mapper=_map_bp_addresses)
    return msg_util.ok_response(data_packet, address_list, resd)


def _map_bp_addresses(result):
    return [{'business_partner_id': row['m01_bp_add_bp_id'],
             'address': {'id': row['m01_add_id'],
                         'address_line_1': row['m01_add_line_1'],
                         'address_line_2': row['m01_add_line_2'],
                         'address_line_3': row['m01_add_line_3'],
                         'city': row['m01_add_city'],
                         'state': row['m01_add_state'],
                         'country': row['m01_add_country'],
                         'zip': row['m01_add_zip']},
             'address_type': {'id': row['m01_add_type_id'],
                              'name': row['m01_add_type_desc_i18n']},
             'note': row['m01_bp_add_note']}
            for row in result]


@message(type='settings.business_partner_update_address',
         version='0.1',
         reqd={'business_partner_id': INTEGER(required=True),
               'address_id': INTEGER(required=True),
               'note': STRING(required=False),
               'address_type_id': INTEGER(required=False)},
         resd={})
@transaction(['lds/BDS'])
def update_address(data_packet):
    db = msg_util.get_db(data_packet)
    reqd = msg_util.get_request(data_packet)['reqd']
    result = db.update('m01_bp_add',
                       db.data_(reqd, 'note', 'address_type_id'),
                       where=db.and_(reqd,
                                     'business_partner_id', 'address_id'),
                       mapping=_request_model_mapping())
    return msg_util.update_response(data_packet, result, update_address)


@message(type='settings.business_partner_remove_address',
         version='0.1',
         reqd={'business_partner_id': INTEGER(required=True),
               'address_id': INTEGER(required=True)},
         resd={})
@transaction(['lds/BDS'])
def remove_address(data_packet):
    db = msg_util.get_db(data_packet)
    reqd = msg_util.get_request(data_packet)['reqd']
    for entry in db.query('m01_bp_add',
                          where=db.and_(reqd, 'address_id',
                                        'business_partner_id'),
                          mapping=_request_model_mapping()):
        db.delete_by_pk('m01_add', entry['add_id'])
        db.delete_by_pk('m01_bp_add', entry['id'])
    return msg_util.delete_response(data_packet, None, remove_address)


##### BUSINESS PARTNER MEDIA CONTACT ##########################################
@message(type='settings.business_partner_add_media_contact',
         version='0.1',
         reqd={'business_partner_id': INTEGER(required=True),
               'media_type_id': INTEGER(required=True),
               'media_contact_id': INTEGER(required=True),
               'note': STRING(required=False)},
         resd={'media_contact_id': INTEGER(required=True),
               'business_partner_id': INTEGER(required=True)})
@transaction(['lds/BDS'])
def add_media(data_packet):
    db = msg_util.get_db(data_packet)
    reqd = msg_util.get_request(data_packet)['reqd']
    result = db.insert('m01_bp_med_con',
                       values=reqd,
                       mapping=_media_type_model_mapping())
    return msg_util.insert_response(data_packet,
                                    result,
                                    add_media,
                                    {'media_contact_id':
                                        reqd['media_contact_id'],
                                     'business_partner_id':
                                        reqd['business_partner_id']})


@message(type='settings.business_partner_media_contact_list',
         version='0.1',
         reqd={'business_partner_id': INTEGER(required=True)},
         resd=[{'business_partner_id': INTEGER(required=True),
                'media_type':{'id': INTEGER(),
                              'name': STRING(required=True)},
                'media_contact':{'id': INTEGER(required=True),
                                 'contact': STRING(required=True)},
                'note': STRING(required=True)}])
@transaction(['lds/BDS'])
def media_list(data_packet):
    db = msg_util.get_db(data_packet)
    reqd = msg_util.get_request(data_packet)['reqd']
    resd = db.query(['m01_bp_med_con', 'm01_med_con', 'm01_med_type'],
                    where=db.and_(reqd, 'business_partner_id'),
                    mapping=_media_type_model_mapping(),
                    result_mapper=_map_bp_media)
    return msg_util.ok_response(data_packet, media_list, resd)


def _map_bp_media(result):
    return [{'business_partner_id':row['m01_bp_med_con_bp_id'],
             'media_type':{'id':row['m01_bp_med_con_med_type_id'],
                           'name':row['m01_med_type_desc_i18n']},
             'media_contact': {'id': row['m01_med_con_id'],
                               'contact': row['m01_med_con_con_str']},
             'note':row['m01_bp_med_con_note']}
            for row in result]


@message(type='settings.business_partner_remove_media_contact',
         version='0.1',
         reqd={'business_partner_id': INTEGER(required=True),
               'media_contact_id': INTEGER(required=True)},
         resd={})
@transaction(['lds/BDS'])
def remove_media(data_packet):
    db = msg_util.get_db(data_packet)
    reqd = msg_util.get_request(data_packet)['reqd']
    db.delete('m01_bp_med_con',
              where=db.and_(reqd, 'media_contact_id', 'business_partner_id'),
              mapping=_media_type_model_mapping())
    return msg_util.delete_response(data_packet, None, remove_media)


@message(type='settings.business_partner_update_media_contact',
         version='0.1',
         reqd={'business_partner_id': INTEGER(required=True),
               'media_contact_id': INTEGER(required=True),
               'media_type_id': INTEGER(required=True),
               'note': STRING(required=False)},
         resd={})
@transaction(['lds/BDS'])
def update_media(data_packet):
    db = msg_util.get_db(data_packet)
    reqd = msg_util.get_request(data_packet)['reqd']
    result = db.update('m01_bp_med_con',
                       db.data_(reqd, 'note', 'media_type_id'),
                       where=db.and_(reqd,
                                     'business_partner_id',
                                     'media_contact_id'),
                       mapping=_media_type_model_mapping())
    return msg_util.update_response(data_packet, result, update_address)
